import os
import ftplib
import ftputil

def ftp_path_isdir(path, ftp):
    """ 同 os.path.isdir """
    try:
        ftp.chdir(path)
        return True
    except:
        return False

def ftp_path_isfile(file, ftp):
    """ 同 os.path.isfile """
    try:
        filename = ftp.path.basename(file)
        if ftp_path_isdir(file.replace(filename, ''), ftp):
            ftp.path.getsize(file)
            return True
        return False
    except:
        return False

def get_local_path(local_path, file_list=None):
    """ 给定一个本地文件夹，递归列出所有文件 """
    files = os.listdir(local_path)
    for file in files:
        temp_path = local_path + os.sep + file
        if not os.path.isdir(temp_path):  # 文件
            file_list.append(temp_path)
        else:  # 文件夹
            get_local_path(temp_path, file_list)
    return file_list

def get_remote_path(ftp, remote_path, file_list=None):
    """ 给定一个 ftp 文件夹，递归列出所有文件 """
    ftp.chdir(remote_path.replace('\\', '/'))  # 解决 ftp.listdir 大小写无法区分问题
    files = ftp.listdir(remote_path.replace('\\', '/'))
    for file in files:
        temp_path = remote_path + '\\' + file
        if not ftp_path_isdir(temp_path.replace('\\', '/'), ftp):  # 文件
            file_list.append(temp_path)
        else:  # 路径
            get_remote_path(ftp, temp_path, file_list)
    return file_list

def upload_tracker(block, src):
    """ 上传回调函数，实现上传进度 """
    global file_write, total_size
    file_write += 64 * 1024
    progress = round((file_write / total_size) * 100)
    if progress >= 100:
        print('Upload ' + src + ' ' + '100%')
    else:
        print('Upload ' + src + ' ' + '%3s%%' % str(progress))

def upload_file(ftp, src, dst):
    """上传文件，src, dst 均以文件名结尾"""
    global file_write, total_size
    file_write = 0
    total_size = os.path.getsize(os.path.abspath(src))
    ftp.upload(src, dst.replace('\\', '/'), lambda block: upload_tracker(block, src))

def download_file(ftp, src, dst):
    """ 下载文件，src、dst 均以文件名结尾 """
    global file_write, total_size
    file_write = 0
    total_size = ftp.path.getsize(src.replace('\\', '/'))
    ftp.download(src.replace('\\', '/'), dst, lambda block: download_tracker(block, src))


def download_tracker(block, src):
    """ 下载回调函数，实现下载进度 """
    global file_write, total_size
    file_write += 64 * 1024
    progress = round((file_write / total_size) * 100)
    if progress >= 100:
        print('Download ' + src + ' ' + '100%')
    else:
        print('Download ' + src + ' ' + '%3s%%' % str(progress))


def upload(ftp, data):
    """ 处理上传，可以是文件列表或文件夹列表，不可以混合上传。 """
    if not ftp_path_isdir(data['remote_path'].replace('\\', '/'), ftp):
        ftp.makedirs(data['remote_path'].replace('\\', '/'))
    is_file, is_dir = False, False
    if 'local_path' in data and data['local_path']:
        # 循环遍历，判断 local_path 中的路径类型，文件还是文件夹
        for local_path in data['local_path']:
            if os.path.isfile(local_path):
                is_file = True
                break
            elif os.path.isdir(local_path):
                is_dir = True
                break
    if is_file:  # 文件
        for filepath in data['local_path']:
            dst = data['remote_path'] + '\\' +  filepath.split('\\')[-1]
            if os.path.exists(filepath):
                upload_file(ftp, filepath, dst)
    elif is_dir:  # 文件夹
        filt_list = []
        for local_path in data['local_path']:
            if os.path.exists(local_path):
                file_list.extend(get_local_path(local_path, []))
        for filepath in file_list:
            dst = data['remote_path'] + '\\' +  filepath.split('\\')[-1]
            if os.path.exists(filepath):
                upload_file(ftp, filepath, dst)


def download(ftp, data):
    """ 处理下载，可以下载文件列表或者文件夹列表，而不可混合下载。 """
    if not os.path.exists(data['local_path']):
        os.makedirs(data['local_path'])
    is_file, is_dir = False, False
    if 'remote_path' in data and data['remote_path']:
        # 循环遍历，判断 remote_path 中的路径类型，文件还是文件夹
        for remote_path in data['remote_path']:
            if ftp_path_isfile(remote_path.replace('\\', '/'), ftp):
                is_file = True
                break
            elif ftp_path_isdir(remote_path.replace('\\', '/'), ftp):
                is_dir = True
                break
    if is_file:  # 文件
        for filepath in data['remote_path']:
            dst = data['local_path'] + '\\' + filepath.split('\\')[-1]
            if ftp_path_isfile(filepath.replace('\\', '/'), ftp):
                download_file(ftp, filepath, dst)
    elif is_dir:  # 文件夹
        file_list = []
        for remote_path in data['remote_path']:
            if ftp_path_isdir(remote_path.replace('\\', '/'), ftp):
                file_list.extend(get_remote_path(ftp, remote_path, []))
        for filepath in file_list:
            dst = data['local_path'] + '\\' + filepath.split('\\')[-1]
            if ftp_path_isfile(filepath.replace('\\', '/'), ftp):
                download_file(ftp, filepath, dst)


class DefaultFTP(ftplib.FTP):
    """ 修改 ftplib 的默认编码 """
    encoding = 'gbk'

if __name__ == '__main__':
    host, username, password = '***.***.***.***', '******', '********'
    ftp = ftputil.FTPHost(host, username, password, session_factory=DefaultFTP)

    data = {"action": "upload", "local_path": ["C:\\TencentFile\\ChenNan_Mod.ma"], "remote_path": "\\test1\\t1\\a1"}
    # data = {"action": "download", "local_path": "C:\\test\\download\\c", "remote_path": ["\\test1\\t1\\a2", "\\test1\\t1\\a1"]}
    print('Start...')
    if data["action"] == 'upload': upload(ftp, data)
    if data["action"] == 'download': download(ftp, data)
    print('End...')
    ftp.close()
